home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / CW GUSI 1.6.4 / src / GUSITCP.cp < prev    next >
Text File  |  1995-04-20  |  25KB  |  978 lines

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSITCP.cp        -    TCP Stream Sockets
  4. Author    :    Matthias Neeracher
  5.  
  6.     This file was derived from the socket library by
  7.  
  8.         Charlie Reiman    <creiman@ncsa.uiuc.edu> and
  9.         Tom Milligan    <milligan@madhaus.utcs.utoronto.ca>
  10.  
  11. Language    :    MPW C/C++
  12.  
  13. $Log: GUSITCP.cp,v $
  14. Revision 1.5  1994/12/30  20:18:41  neeri
  15. Wake up process from completion procedures.
  16. Minor corrections.
  17.  
  18. Revision 1.4  1994/08/10  00:04:13  neeri
  19. Sanitized for universal headers.
  20.  
  21. Revision 1.3  1994/05/04  01:47:24  neeri
  22. Long writes on nonblocking sockets would fail.
  23.  
  24. Revision 1.2  1994/05/01  23:48:59  neeri
  25. Enable recvfrom with non-null from address.
  26. Try to behave better when other side closes connection.
  27.  
  28. Revision 1.1  1994/02/25  02:30:45  neeri
  29. Initial revision
  30.  
  31. Revision 0.6  1993/08/25  00:00:00  neeri
  32. return correct peer address from accept()
  33.  
  34. Revision 0.5  1993/01/31  00:00:00  neeri
  35. Support for inetd
  36.  
  37. Revision 0.4  1993/01/21  00:00:00  neeri
  38. Simplify and correct code
  39.  
  40. Revision 0.3  1993/01/17  00:00:00  neeri
  41. Be more careful about user interrupts.
  42.  
  43. Revision 0.2  1993/01/08  00:00:00  neeri
  44. tcp_notify was setting the wrong state to unconnected
  45.  
  46. Revision 0.1  1992/09/08  00:00:00  neeri
  47. A big part should work now                
  48.  
  49. *********************************************************************/
  50.  
  51. #include "GUSIINET_P.h"
  52.  
  53. #if GENERATING68K
  54. #pragma segment GUSIINET
  55. #endif
  56.  
  57. /********************** Completion procedures ***********************/
  58.  
  59. pascal void tcp_notify(
  60.     StreamPtr,
  61.     u_short                    eventCode,
  62.     Ptr                        userDataPtr,
  63.     u_short,
  64.     struct ICMPReport *)
  65. {
  66.     TCPSocket *    sock    =    *(TCPSocket **) userDataPtr;
  67.  
  68.     switch (eventCode) {
  69.     case TCPClosing:
  70.         if (sock->sstate == SOCK_STATE_LISTENING || sock->sstate == SOCK_STATE_LIS_CON)
  71.             sock->sstate = SOCK_STATE_LIS_CLOSE;
  72.         else
  73.             sock->sstate = SOCK_STATE_CLOSING;
  74.         break;
  75.  
  76.     case TCPTerminate:
  77.         if (sock->sstate != SOCK_STATE_LIS_CLOSE)
  78.             if (sock->sstate == SOCK_STATE_LISTENING || sock->sstate == SOCK_STATE_LIS_CON)
  79.                 sock->sstate = SOCK_STATE_LIS_CLOSE;
  80.             else
  81.                 sock->sstate = SOCK_STATE_UNCONNECTED;
  82.         break;
  83.     }
  84.     sock->Ready();
  85. }
  86.  
  87. #if GENERATINGCFM
  88. RoutineDescriptor    u_tcp_notify = 
  89.         BUILD_ROUTINE_DESCRIPTOR(uppTCPNotifyProcInfo, tcp_notify);
  90. #else
  91. #define u_tcp_notify tcp_notify
  92. #endif
  93.  
  94. void tcp_connect_done(AnnotatedPB *pb)
  95. {
  96.     TCPSocket *    sock    =    (TCPSocket *) pb->Owner();
  97.     TCPiopb *    tcp    =    pb->TCP();
  98.  
  99.     if (!(sock->asyncerr = tcp->ioResult)) {
  100.         sock->sa.sin_addr.s_addr     = tcp->csParam.open.localHost;
  101.         sock->sa.sin_port             = tcp->csParam.open.localPort;
  102.         sock->peer.sin_addr.s_addr    = tcp->csParam.open.remoteHost;
  103.         sock->peer.sin_port             = tcp->csParam.open.remotePort;
  104.         sock->sstate                     = SOCK_STATE_CONNECTED;
  105.     }
  106.     sock->Ready();
  107. }
  108.  
  109. #if GENERATINGCFM
  110. RoutineDescriptor    u_tcp_connect_done = 
  111.         BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo, tcp_connect_done);
  112. #else
  113. #define u_tcp_connect_done tcp_connect_done
  114. #endif
  115.  
  116. void tcp_listen_done(AnnotatedPB *pb)
  117. {
  118.     TCPSocket *    sock    =    (TCPSocket *) pb->Owner();
  119.     TCPiopb *    tcp    =    pb->TCP();
  120.  
  121.     switch(tcp->ioResult) {
  122.     case noErr:
  123.         sock->peer.sin_addr.s_addr    = tcp->csParam.open.remoteHost;
  124.         sock->peer.sin_port             = tcp->csParam.open.remotePort;
  125.         sock->sstate                     = SOCK_STATE_LIS_CON;
  126.         sock->asyncerr                 = 0;
  127.         break;
  128.  
  129.     case openFailed:
  130.     case invalidStreamPtr:
  131.     case connectionExists:
  132.     case duplicateSocket:
  133.     case commandTimeout:
  134.     default:
  135.         sock->sstate                     = SOCK_STATE_LIS_CLOSE;
  136.         sock->asyncerr                 = tcp->ioResult;
  137.         break;
  138.     }
  139.     sock->Ready();
  140. }
  141.  
  142. #if GENERATINGCFM
  143. RoutineDescriptor    u_tcp_listen_done = 
  144.         BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo, tcp_listen_done);
  145. #else
  146. #define u_tcp_listen_done tcp_listen_done
  147. #endif
  148.  
  149. void tcp_recv_done(AnnotatedPB *pb)
  150. {
  151.     TCPSocket *        sock    =    (TCPSocket *) pb->Owner();
  152.     TCPiopb *        tcp    =    pb->TCP();
  153.     register    int     readin;
  154.  
  155.     if (!tcp->ioResult) {
  156.         readin = tcp->csParam.receive.rcvBuffLen;
  157.         sock->recvd   = readin;
  158.     }
  159.     sock->Ready();
  160. }
  161.  
  162. #if GENERATINGCFM
  163. RoutineDescriptor    u_tcp_recv_done = 
  164.         BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo, tcp_recv_done);
  165. #else
  166. #define u_tcp_recv_done tcp_recv_done
  167. #endif
  168.  
  169. void tcp_send_done(AnnotatedPB *pb)
  170. {
  171.     TCPSocket *    sock    =    (TCPSocket *) pb->Owner();
  172.     TCPiopb *    tcp    =    pb->TCP();
  173.  
  174.     switch (tcp->ioResult) {
  175.     case noErr:
  176.         ((wdsEntry *)(tcp->csParam.send.wdsPtr))->length = 0;    /* mark it free */
  177.         sock->asyncerr = noErr;
  178.         break;
  179.  
  180.     case ipNoFragMemErr:
  181.     case connectionClosing:
  182.     case connectionTerminated:
  183.     case connectionDoesntExist:
  184.         sock->sstate     = SOCK_STATE_UNCONNECTED;
  185.         sock->asyncerr = ENOTCONN;
  186.         break;
  187.  
  188.     case ipDontFragErr:
  189.     case invalidStreamPtr:
  190.     case invalidLength:
  191.     case invalidWDS:
  192.     default:
  193.         sock->sstate     = SOCK_STATE_UNCONNECTED;
  194.         sock->asyncerr = tcp->ioResult;
  195.         break;
  196.     }
  197.     sock->Ready();
  198. }
  199.  
  200. #if GENERATINGCFM
  201. RoutineDescriptor    u_tcp_send_done = 
  202.         BUILD_ROUTINE_DESCRIPTOR(uppTCPIOCompletionProcInfo, tcp_send_done);
  203. #else
  204. #define u_tcp_send_done tcp_send_done
  205. #endif
  206.  
  207. /************************ TCPSocket members *************************/
  208.  
  209.  
  210. TCPSocket::TCPSocket()
  211.     :    INETSocket()
  212. {
  213.     TCPiopb    pb;
  214.  
  215.     sstate     = SOCK_STATE_UNCONNECTED;
  216.     self          = new(TCPSocket *);
  217.     *self        = this;
  218.  
  219.     pb.ioCRefNum                         = INETSockets.Driver();
  220.     pb.csCode                             = TCPCreate;
  221.     pb.csParam.create.rcvBuff         = (char *)NewPtr(STREAM_BUFFER_SIZE);
  222.     pb.csParam.create.rcvBuffLen     = STREAM_BUFFER_SIZE;
  223.     pb.csParam.create.notifyProc     = TCPNotifyUPP(&u_tcp_notify);
  224.     pb.csParam.create.userDataPtr    = Ptr(self);
  225.  
  226.    if (!pb.csParam.create.rcvBuff) {
  227.         GUSI_error(ENOBUFS);
  228.         return;
  229.     }
  230.         
  231.     switch(PBControlSync(ParmBlkPtr(&pb)))
  232.     {
  233.         case noErr:                     break;
  234.         case invalidLength:             GUSI_error(ENOBUFS);     return;
  235.         case invalidBufPtr:             GUSI_error(ENOBUFS);     return;
  236.         case insufficientResources:     GUSI_error(EMFILE);         return;
  237.         default:                             GUSI_error(ENETDOWN);     return;
  238.     }
  239.  
  240.     peer.sin_family         = AF_INET;
  241.     peer.sin_addr.s_addr = 0;
  242.     peer.sin_port             = 0;
  243.  
  244.     bzero(&peer.sin_zero[0], 8);
  245.  
  246.     asyncerr                 = 0;
  247.     stream                     = pb.tcpStream;
  248. }
  249.  
  250. TCPSocket::TCPSocket(StreamPtr stream)
  251.     :    INETSocket(stream)
  252. {
  253.     AppleEvent                theEvent, myReply;
  254.     AEDesc                    theAddress;
  255.     long                        theType = 'inet';
  256.     ProcPtr                    theProc    = ProcPtr(&u_tcp_notify);
  257.     ProcessSerialNumber    PSN;
  258.  
  259.     self          = new(TCPSocket *);
  260.     *self        = this;
  261.     asyncerr = 0;
  262.  
  263.     GetCurrentProcess(&PSN);
  264.     AECreateDesc(typeApplSignature, (Ptr) &theType, sizeof(theType), &theAddress);
  265.     AECreateAppleEvent('INET', 'TNFY',  &theAddress, kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
  266.     
  267.     AEPutParamPtr(&theEvent, 'STRM', typeLongInteger, (Ptr) &stream, sizeof(stream));
  268.     AEPutParamPtr(&theEvent, 'ASR ', typeLongInteger, (Ptr) &theProc, sizeof(ProcPtr));
  269.     AEPutParamPtr(&theEvent, 'USRP', typeLongInteger, (Ptr) &self, sizeof(long));
  270.     AEPutParamPtr(&theEvent, keyProcessSerialNumber, typeProcessSerialNumber, (Ptr) &PSN, sizeof(ProcessSerialNumber));
  271.     
  272.     AESend(&theEvent, &myReply, kAEWaitReply, kAEHighPriority, 120, nil, nil);
  273.  
  274.     AEDisposeDesc(&myReply);
  275.     AEDisposeDesc(&theEvent);
  276.     AEDisposeDesc(&theAddress);
  277.  
  278.     TCPiopb * pb;
  279.  
  280.     pb                = GetPB();
  281.     pb->csCode     = TCPStatus;
  282.  
  283.     PBControlSync(ParmBlkPtr(pb));
  284.     
  285.     sa.sin_addr.s_addr     =     pb->csParam.status.localHost;
  286.     sa.sin_port                =    pb->csParam.status.localPort;
  287.     peer.sin_addr.s_addr =     pb->csParam.status.remoteHost;
  288.     peer.sin_port            =    pb->csParam.status.remotePort;
  289. }
  290.  
  291. TCPSocket::TCPSocket(TCPSocket * sock)
  292. {
  293.     stream        = sock->stream;
  294.     status        = sock->status;
  295.     nonblocking    = sock->nonblocking;
  296.     recvBuf        = sock->recvBuf;
  297.     recvd            = sock->recvd;
  298.     sa                = sock->sa;
  299.     peer            = sock->peer;
  300.     sstate        = sock->sstate;
  301.     asyncerr        = 0;
  302.     
  303.     // The reason for this strange code is that stream->userData points to
  304.     // sock.self and cannot be changed while the stream is alive
  305.     
  306.     self            = sock->self;
  307.     *self            = this;
  308.     sock->self    = new(TCPSocket *);
  309.     *sock->self    = sock;
  310. }
  311.  
  312. TCPSocket::~TCPSocket()
  313. {
  314.     TCPiopb *    pb;
  315.  
  316.     do {
  317.         pb                    = GetPB();
  318.         pb->csCode         = TCPStatus;
  319.  
  320.         PBControlSync(ParmBlkPtr(pb));
  321.  
  322.         SAFESPIN(false, SP_MISC, 0);
  323.     } while (!errno && pb->csParam.status.amtUnackedData > 0);
  324.  
  325.     pb                                                = GetPB();
  326.     pb->ioCompletion                            = nil;
  327.     pb->csCode                                     = TCPClose;
  328.     pb->csParam.close.validityFlags         = timeoutValue | timeoutAction;
  329.     pb->csParam.close.ulpTimeoutValue     = 60 /* seconds */;
  330.     pb->csParam.close.ulpTimeoutAction     = 1 /* 1:abort 0:report */;
  331.  
  332.     switch (PBControlAsync(ParmBlkPtr(pb)))
  333.     {
  334.         case noErr:
  335.         case connectionClosing:
  336.             break;
  337.         case connectionDoesntExist:
  338.         case connectionTerminated:
  339.             break;
  340.         case invalidStreamPtr:
  341.         default:
  342.             return;
  343.     }
  344.  
  345.     {
  346.         rdsEntry    rdsarray[TCP_MAX_WDS+1];
  347.         int        passcount;
  348.         const int maxpass =4;
  349.  
  350.         pb                    =    GetPB();
  351.  
  352.         for (passcount=0; passcount<maxpass; passcount++) {
  353.             pb->csCode                                             = TCPNoCopyRcv;
  354.             pb->csParam.receive.commandTimeoutValue    = 1; /* seconds, 0 = blocking */
  355.             pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  356.             pb->csParam.receive.rdsLength                 = TCP_MAX_WDS;
  357.  
  358.             if (PBControlSync(ParmBlkPtr(pb)))
  359.                 break;
  360.  
  361.             pb->csCode                                             = TCPRcvBfrReturn;
  362.             pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  363.  
  364.             PBControlSync(ParmBlkPtr(pb));
  365.  
  366.             SAFESPIN(false, SP_MISC, 0);
  367.             
  368.             if (errno)
  369.                 break;
  370.         }
  371.  
  372.         if (passcount == maxpass) {        /* remote side isn't being nice */
  373.             /* then try again */
  374.  
  375.             PBControlSync(ParmBlkPtr(pb));
  376.  
  377.             for (passcount=0; passcount<maxpass; passcount++) {
  378.                 pb->csCode                                             = TCPNoCopyRcv;
  379.                 pb->csParam.receive.commandTimeoutValue    = 1; /* seconds, 0 = blocking */
  380.                 pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  381.                 pb->csParam.receive.rdsLength                 = TCP_MAX_WDS;
  382.  
  383.                 if (PBControlSync(ParmBlkPtr(pb)))
  384.                     break;
  385.  
  386.                 pb->csCode                                             = TCPRcvBfrReturn;
  387.                 pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  388.  
  389.                 PBControlSync(ParmBlkPtr(pb));
  390.  
  391.                 SAFESPIN(false, SP_MISC, 0);
  392.                 
  393.                 if (errno)
  394.                     break;
  395.             }
  396.         }
  397.     }
  398.  
  399.     /* destroy the stream */
  400.     pb->csCode     = TCPRelease;
  401.  
  402.     if (PBControlSync(ParmBlkPtr(pb)))
  403.         return;
  404.  
  405.     DisposPtr(pb->csParam.create.rcvBuff); /* there is no release pb */
  406.     
  407.     delete self;
  408. }
  409.  
  410. TCPiopb * TCPSocket::GetPB()
  411. {
  412.     AnnotatedPB *    pb        =    INETSockets.GetPB();
  413.     pb->TCP()->ioCRefNum =    INETSockets.Driver();
  414.     pb->TCP()->tcpStream    =    stream;
  415.  
  416.     pb->SetOwner(this);
  417.  
  418.     return pb->TCP();
  419. }
  420.  
  421. u_long TCPSocket::Available()
  422. {
  423.     TCPiopb * pb;
  424.  
  425.     pb                = GetPB();
  426.     pb->csCode     = TCPStatus;
  427.  
  428.     if (PBControlSync(ParmBlkPtr(pb)))
  429.         return 0;
  430.     else
  431.         return pb->csParam.status.amtUnreadData;
  432. }
  433.  
  434. /*
  435.  *    connect - initiate a connection on a MacTCP socket
  436.  *
  437.  *        This call attempts to make a
  438.  *        connection to another socket. The other socket is specified
  439.  *        by an internet address and port.
  440.  *
  441.  *        TCP sockets may successfully connect() only once;
  442.  *
  443.  *        If the connection or binding succeeds, then 0 is returned.
  444.  *        Otherwise a -1 is returned, and a more specific error code
  445.  *        is stored in errno.
  446.  *
  447.  *        EAFNOSUPPORT        The address family in addr is not AF_INET.
  448.  *
  449.  *        EHOSTUNREACH        The TCP connection came up half-way and
  450.  *                          then failed.
  451.  */
  452.  
  453. int TCPSocket::connect(void * address, int addrlen)
  454. {
  455.     OSErr                        err;
  456.     struct sockaddr_in *    addr    =    (struct sockaddr_in *) address;
  457.     TCPiopb    *                pb;
  458.  
  459.     if (addrlen != int(sizeof(struct sockaddr_in)))
  460.         return GUSI_error(EINVAL);
  461.  
  462.     if (addr->sin_family != AF_INET)
  463.         return GUSI_error(EAFNOSUPPORT);
  464.  
  465.     /* Make sure this socket can connect. */
  466.     if (sstate == SOCK_STATE_CONNECTING)
  467.         if (asyncerr)
  468.             return GUSI_error(ECONNREFUSED);
  469.         else
  470.             return GUSI_error(EALREADY);
  471.     if (sstate != SOCK_STATE_UNCONNECTED)
  472.         return GUSI_error(EISCONN);
  473.  
  474.     sstate = SOCK_STATE_CONNECTING;
  475.  
  476.     pb                                                    = GetPB();
  477.     pb->ioCompletion                                = TCPIOCompletionUPP(&u_tcp_connect_done);
  478.     pb->csCode                                         = TCPActiveOpen;
  479.     pb->csParam.open.validityFlags             = timeoutValue | timeoutAction;
  480.     pb->csParam.open.ulpTimeoutValue         = 60 /* seconds */;
  481.     pb->csParam.open.ulpTimeoutAction         = 1 /* 1:abort 0:report */;
  482.     pb->csParam.open.commandTimeoutValue     = 0;
  483.     pb->csParam.open.remoteHost                 = addr->sin_addr.s_addr;
  484.     pb->csParam.open.remotePort                 = addr->sin_port;
  485.     pb->csParam.open.localHost                 = 0;
  486.     pb->csParam.open.localPort                 = sa.sin_port;
  487.     pb->csParam.open.dontFrag                     = 0;
  488.     pb->csParam.open.timeToLive                 = 0;
  489.     pb->csParam.open.security                     = 0;
  490.     pb->csParam.open.optionCnt                 = 0;
  491.  
  492.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  493.     {
  494.         sstate = SOCK_STATE_UNCONNECTED;
  495.         return TCP_error(err);
  496.     }
  497.  
  498.     if (nonblocking)
  499.         return GUSI_error(EINPROGRESS);
  500.  
  501.     /* sync connect - spin till TCPActiveOpen completes */
  502.  
  503.     SAFESPIN(pb->ioResult==inProgress, SP_MISC, 0);
  504.  
  505.     if (errno || pb->ioResult) {
  506.         sstate = SOCK_STATE_UNCONNECTED;
  507.         
  508.         if (errno)
  509.             return -1;
  510.         else
  511.             return TCP_error(pb->ioResult);
  512.     } else
  513.         return 0;
  514. }
  515.  
  516. int TCPSocket::listen(int)
  517. {
  518.     OSErr            err;
  519.     TCPiopb *    pb;
  520.  
  521.     if (sstate != SOCK_STATE_UNCONNECTED)
  522.         return GUSI_error(EISCONN);
  523.  
  524.     sstate                                             = SOCK_STATE_LISTENING;
  525.     pb                                                    = GetPB();
  526.     pb->ioCRefNum                                     = INETSockets.Driver();
  527.     pb->ioCompletion                                = TCPIOCompletionUPP(&u_tcp_listen_done);
  528.     pb->csCode                                         = TCPPassiveOpen;
  529.     pb->csParam.open.validityFlags             = timeoutValue | timeoutAction;
  530.     pb->csParam.open.ulpTimeoutValue         = 255 /* seconds */;
  531.     pb->csParam.open.ulpTimeoutAction         = 0 /* 1:abort 0:report */;
  532.     pb->csParam.open.commandTimeoutValue     = 0 /* infinity */;
  533.     pb->csParam.open.remoteHost                 = 0;
  534.     pb->csParam.open.remotePort                 = 0;
  535.     pb->csParam.open.localHost                 = 0;
  536.     pb->csParam.open.localPort                 = sa.sin_port;
  537.     pb->csParam.open.dontFrag                     = 0;
  538.     pb->csParam.open.timeToLive                 = 0;
  539.     pb->csParam.open.security                     = 0;
  540.     pb->csParam.open.optionCnt                 = 0;
  541.  
  542.     if (err = PBControlAsync(ParmBlkPtr(pb))) {
  543.         sstate = SOCK_STATE_UNCONNECTED;
  544.         
  545.         return TCP_error(err);
  546.     }
  547.     
  548.     SAFESPIN(!pb->csParam.open.localPort, SP_MISC, 0);
  549.         
  550.     if (errno) {
  551.         sstate = SOCK_STATE_UNCONNECTED;
  552.         
  553.         return -1;
  554.     }
  555.  
  556.     sa.sin_addr.s_addr     = pb->csParam.open.localHost;
  557.     sa.sin_port             = pb->csParam.open.localPort;
  558.  
  559.     return 0;
  560. }
  561.  
  562. Socket * TCPSocket::accept(void *from, int *fromlen)
  563. {
  564.     TCPSocket *        sock;
  565.     TCPiopb *        pb;
  566.  
  567.     if (sstate == SOCK_STATE_UNCONNECTED)
  568.         if (asyncerr) {
  569.             TCP_error(asyncerr);
  570.             asyncerr = 0;
  571.  
  572.             return nil;
  573.         } else
  574.             return (Socket *) GUSI_error_nil(ENOTCONN);
  575.  
  576.     if (sstate != SOCK_STATE_LISTENING && sstate != SOCK_STATE_LIS_CON && sstate != SOCK_STATE_LIS_CLOSE)
  577.         return (Socket *) GUSI_error_nil(ENOTCONN);
  578.  
  579.     if (sstate == SOCK_STATE_LISTENING) {
  580.         if (nonblocking)
  581.             return (Socket *) GUSI_error_nil(EWOULDBLOCK);
  582.  
  583.         /*    Spin till sock_tcp_listen_done runs. */
  584.         SPINP(sstate == SOCK_STATE_LISTENING, SP_MISC, 0);
  585.  
  586.         /* got notification - was it success? */
  587.         if (sstate != SOCK_STATE_LIS_CON && sstate != SOCK_STATE_LIS_CLOSE) {
  588.             (void) TCP_error(asyncerr);
  589.             asyncerr = 0;
  590.             return nil;
  591.         }
  592.     }
  593.  
  594.     /*
  595.      * Have connection.  Duplicate this socket.  The client gets the connection
  596.      * on the new socket and I create a new stream on the old socket and put it
  597.      * in listen state.
  598.      */
  599.     sstate     = (sstate == SOCK_STATE_LIS_CON) ? SOCK_STATE_CONNECTED : SOCK_STATE_UNCONNECTED;
  600.     sock        = new TCPSocket(this);
  601.  
  602.     if (!sock)
  603.     {
  604.         /*    Abort the incoming connection. */
  605.         pb                 = GetPB();
  606.         pb->csCode         = TCPAbort;
  607.  
  608.         PBControlSync(ParmBlkPtr(pb));
  609.  
  610.         sstate = SOCK_STATE_UNCONNECTED;
  611.  
  612.         /* try and put the socket back in listen mode */
  613.         if (listen(5) < 0)
  614.         {
  615.             sstate = SOCK_STATE_UNCONNECTED;
  616.             return nil;        /* errno already set */
  617.         }
  618.         return (Socket *) GUSI_error_nil(ENOMEM);
  619.     }
  620.  
  621.     /* Create a new MacTCP stream on the old socket and put it into */
  622.     /* listen state to accept more connections. */
  623.     sstate = SOCK_STATE_UNCONNECTED;
  624.  
  625.     pb                                            = GetPB();
  626.     pb->csCode                                 = TCPCreate;
  627.     pb->csParam.create.rcvBuff         = (char *)NewPtr(STREAM_BUFFER_SIZE);
  628.     pb->csParam.create.rcvBuffLen     = STREAM_BUFFER_SIZE;
  629.     pb->csParam.create.notifyProc     = TCPNotifyUPP(&u_tcp_notify);
  630.     pb->csParam.create.userDataPtr    = Ptr(self);
  631.  
  632.    if (!pb->csParam.create.rcvBuff)
  633.         return (Socket *) GUSI_error_nil(ENOBUFS);
  634.  
  635.     switch(PBControlSync(ParmBlkPtr(pb)))
  636.     {
  637.         case noErr:                     break;
  638.         case invalidLength:             return (Socket *) GUSI_error_nil(ENOBUFS);
  639.         case invalidBufPtr:             return (Socket *) GUSI_error_nil(ENOBUFS);
  640.         case insufficientResources:     return (Socket *) GUSI_error_nil(EMFILE);
  641.         default:                             return (Socket *) GUSI_error_nil(ENETDOWN);
  642.     }
  643.  
  644.     peer.sin_family         = AF_INET;
  645.     peer.sin_addr.s_addr = 0;
  646.     peer.sin_port             = 0;
  647.  
  648.     bzero(&peer.sin_zero[0], 8);
  649.  
  650.     asyncerr                 = 0;
  651.     stream                     = pb->tcpStream;
  652.  
  653.     if (listen(5) < 0) {
  654.         /* nothing to listen on */
  655.         sstate = SOCK_STATE_UNCONNECTED;
  656.  
  657.         /* kill the incoming connection */
  658.         pb                    = sock->GetPB();
  659.         pb->csCode        = TCPRelease;
  660.  
  661.         if (!PBControlSync(ParmBlkPtr(pb)))
  662.             DisposPtr(pb->csParam.create.rcvBuff); /* there is no release pb */
  663.  
  664.         return nil; /* errno set */
  665.     }
  666.  
  667.     /* return address of partner */
  668.     memcpy(from, &sock->peer, *fromlen = int(min(*fromlen, int(sizeof(struct sockaddr_in)))));
  669.  
  670.     return sock;
  671. }
  672.  
  673. /*
  674.  *    TCPSocket::recvfrom(s, buffer, buflen, flags, from, fromlen)
  675.  *
  676.  *        recvfrom() attempts to receive a message (ie a datagram)
  677.  *        on the socket s.
  678.  *
  679.  *        from returns the address of the socket which sent the message.
  680.  *        fromlen is the usual value-result length parameter.
  681.  *
  682.  *        Typically, read() is used with a TCP stream and recv() with
  683.  *        UDP where the idea of a message makes more sense. But in fact,
  684.  *        read() and recv() are equivalent.
  685.  *
  686.  *        Regardless of non-blocking status, if less data is available
  687.  *        than has been requested, only that much data is returned.
  688.  *
  689.  *        If the socket is marked for non-blocking I/O, and the socket
  690.  *        is empty, the operation will fail with the error EWOULDBLOCK.
  691.  *        Otherwise, the operation will block until data is available
  692.  *        or an error occurs.
  693.  *
  694.  *        A return value of zero indicates that the stream has been
  695.  *        closed and all data has already been read. ie. end-of-file.
  696.  *
  697.  *        Flags is ignored.
  698.  *
  699.  *        If successful, the number of bytes actually received is
  700.  *        returned. Otherwise, a -1 is returned and the global variable
  701.  *        errno is set to indicate the error.
  702.  *
  703.  *        ESHUTDOWN    The socket has been shutdown for receive operations.
  704.  */
  705.  
  706. int TCPSocket::recvfrom(void * buffer, int buflen, int, void * from, int * fromlen)
  707. {
  708.     TCPiopb    *    pb;
  709.     u_long        dataavail;
  710.  
  711.     if (from)
  712.         getpeername(from, fromlen);
  713.     if (status & SOCK_STATUS_NOREAD)
  714.         return GUSI_error(ESHUTDOWN);
  715.  
  716.     /* socket hasn't finished connecting yet */
  717.     if (sstate == SOCK_STATE_CONNECTING)
  718.     {
  719.         if (nonblocking)
  720.             return GUSI_error(EWOULDBLOCK);
  721.  
  722.         /* async connect and sync recv? */
  723.  
  724.         SPIN(sstate == SOCK_STATE_CONNECTING,SP_MISC,0);
  725.     }
  726.  
  727.     /* socket is not connected */
  728.     if (!(sstate == SOCK_STATE_CONNECTED))
  729.     {
  730.         if (sstate == SOCK_STATE_CLOSING)
  731.             return 0;
  732.             
  733.         /* see if the connect died (pretty poor test) */
  734.         if (sstate == SOCK_STATE_UNCONNECTED && asyncerr != 0 && asyncerr != 1)
  735.         {
  736.             (void) TCP_error(asyncerr);
  737.             asyncerr = 0;
  738.             return -1;
  739.         }
  740.  
  741.         /* I guess he just forgot */
  742.         return GUSI_error(ENOTCONN);
  743.     }
  744.  
  745.     dataavail = Available();
  746.  
  747.     if (nonblocking && !dataavail)
  748.         return GUSI_error(EWOULDBLOCK);
  749.         
  750.     recvBuf    = (char *) buffer;
  751.     recvd        = 0;
  752.     asyncerr    = inProgress;
  753.  
  754.     pb                                                     = GetPB();
  755.     pb->ioCompletion                                    = TCPIOCompletionUPP(&u_tcp_recv_done);
  756.     pb->csCode                                             = TCPRcv;
  757.     pb->csParam.receive.commandTimeoutValue     = 0; /* seconds, 0 = blocking */
  758.     pb->csParam.receive.rcvBuff                     = recvBuf;
  759.     pb->csParam.receive.rcvBuffLen                 = min(buflen,TCP_MAX_MSG);
  760.  
  761.     PBControlAsync(ParmBlkPtr(pb));
  762.  
  763.     /* This is potentially dangerous, as there doesn't seem to be a way to
  764.         stop the receive call on an user abort.
  765.     */
  766.     SPIN(pb->ioResult==inProgress, SP_STREAM_READ, buflen);
  767.  
  768.     if (pb->ioResult == commandTimeout)
  769.         pb->ioResult = noErr;
  770.  
  771.     switch(pb->ioResult)
  772.     {
  773.         case noErr:
  774.             asyncerr = noErr;
  775.  
  776.             return recvd;
  777.  
  778.         case connectionClosing:
  779.         case connectionTerminated:
  780.             return recvd;
  781.  
  782.         case commandTimeout: /* this one should be caught by sock_tcp_recv_done */
  783.         case connectionDoesntExist:
  784.         case invalidStreamPtr:
  785.         case invalidLength:
  786.         case invalidBufPtr:
  787.         default:
  788.             return TCP_error(pb->ioResult);
  789.     }
  790. }
  791.  
  792. /*
  793.  *    TCPSocket::sendto(s, buffer, buflen, flags, to, tolen)
  794.  *
  795.  *        sendto() is used to transmit a message to another
  796.  *        socket on the socket s.
  797.  *
  798.  *        Typically, write() is used with a TCP stream and send() with
  799.  *        UDP where the idea of a message makes more sense. But in fact,
  800.  *        write() and send() are equivalent.
  801.  *
  802.  *        Write() and send() operations are not considered complete
  803.  *        until all data has been sent and acknowledged.
  804.  *
  805.  *        If a socket is marked for non-blocking I/O, the operation
  806.  *        will return an 'error' of EINPROGRESS.
  807.  *
  808.  *        If the socket is not marked for non-blocking I/O, the write will
  809.  *        block until space becomes available.
  810.  *
  811.  *        write() and send() may be used only when the socket is in a connected
  812.  *        state, sendto() may be used at any time.
  813.  *
  814.  *        Flags is ignored.
  815.  *
  816.  *        These calls return the number of bytes sent, or -1 if an error
  817.  *        occurred.
  818.  *
  819.  *        EINVAL              The sum of the iov_len values in the iov array was
  820.  *                                greater than 65535 (TCP) or 65507 (UDP) or there
  821.  *                      were too many entries in the array (16 for TCP or
  822.  *                      6 for UDP).
  823.  *
  824.  *        ESHUTDOWN        The socket has been shutdown for send operations.
  825.  *
  826.  *        EMSGSIZE         The message is too big to send in one datagram. (UDP)
  827.  *
  828.  *        ENOBUFS          The transmit queue is full. (UDP)
  829.  */
  830.  
  831. int TCPSocket::sendto(void * buffer, int count, int flags, void * to, int)
  832. {
  833.     int            bytes,towrite;
  834.     miniwds *    thiswds;
  835.     short            wdsnum;
  836.     TCPiopb *    pb;
  837.     miniwds        wdsarray[TCP_MAX_WDS];
  838.  
  839.     if (status & SOCK_STATUS_NOWRITE)
  840.         return GUSI_error(ESHUTDOWN);
  841.  
  842.     if (to != NULL) /* sendto */
  843.         return GUSI_error(EOPNOTSUPP);
  844.     if (sstate != SOCK_STATE_CONNECTED && sstate != SOCK_STATE_CONNECTING)
  845.         return GUSI_error(ENOTCONN);
  846.  
  847.     /* socket hasn't finished connecting yet */
  848.     if (sstate == SOCK_STATE_CONNECTING) {
  849.         if (nonblocking)
  850.             return GUSI_error(EALREADY);
  851.  
  852.         /* async connect and sync send? */
  853.         SPIN(sstate == SOCK_STATE_CONNECTING, SP_MISC, 0);
  854.     }
  855.  
  856.     /* socket is not connected */
  857.     if (!(sstate == SOCK_STATE_CONNECTED)) {
  858.         /* see if a previous operation failed */
  859.         if (sstate == SOCK_STATE_UNCONNECTED && asyncerr != 0) {
  860.             (void) TCP_error(asyncerr);
  861.             asyncerr = 0;
  862.             return -1;
  863.         }
  864.  
  865.         /* I guess he just forgot */
  866.         return GUSI_error(ENOTCONN);
  867.     }
  868.  
  869.     pb                    = GetPB();
  870.     pb->csCode         = TCPStatus;
  871.  
  872.     if (PBControlSync(ParmBlkPtr(pb)))
  873.         bytes = 0;
  874.     else {
  875.         bytes = pb->csParam.status.sendWindow - pb->csParam.status.amtUnackedData;
  876.  
  877.         if (bytes < 0)
  878.             bytes = 0;
  879.     }
  880.  
  881.     if (nonblocking)
  882.         if (!bytes)
  883.             return GUSI_error(EWOULDBLOCK);
  884.         else if (bytes < count)
  885.             count = bytes;
  886.  
  887.     bytes    =    count;                                                /* save count before we nuke it */
  888.     memset(wdsarray, 0, TCP_MAX_WDS*sizeof(miniwds));    /* clear up terminus and mark empty */
  889.     thiswds = wdsarray;
  890.     wdsnum = 0;
  891.  
  892.     while (count > 0) {
  893.         /* make sure the thing that just finished worked ok */
  894.         if (asyncerr) {
  895.             (void) GUSI_error(asyncerr);
  896.             asyncerr = 0;
  897.             return -1;
  898.         }
  899.  
  900.         towrite=min(count,TCP_MAX_MSG);
  901.  
  902.         /* find a clean wds */
  903.  
  904.         while (thiswds->length != 0) {
  905.             wdsnum = (short)((wdsnum+1)%TCP_MAX_WDS); /* generates compiler warning w/o short - why? */
  906.             if (wdsnum)
  907.                 thiswds++;
  908.             else
  909.                 thiswds = wdsarray;
  910.             SPIN(false, SP_STREAM_WRITE, count);    /* spin once */
  911.         }
  912.  
  913.         /* find a clean pb */
  914.  
  915.         thiswds->length                            = (short)towrite;
  916.         thiswds->ptr                                = (char *) buffer;
  917.         pb                                                = GetPB();
  918.         pb->ioCompletion                            = TCPIOCompletionUPP(&u_tcp_send_done);
  919.         pb->csCode                                     = TCPSend;
  920.         pb->csParam.send.validityFlags         = timeoutValue | timeoutAction;
  921.         pb->csParam.send.ulpTimeoutValue     = 60 /* seconds */;
  922.         pb->csParam.send.ulpTimeoutAction     = 1 /* 0:abort 1:report */;
  923.         pb->csParam.send.pushFlag                 = count <= TCP_MAX_MSG;
  924.         pb->csParam.send.urgentFlag             = flags & MSG_OOB;
  925.         pb->csParam.send.wdsPtr                 = (Ptr)thiswds;
  926.         pb->csParam.send.sendFree                 = 0;
  927.         pb->csParam.send.sendLength             = 0;
  928.  
  929.         PBControlAsync(ParmBlkPtr(pb));
  930.  
  931.         SPIN(false, SP_STREAM_WRITE, count);
  932.         count     -= towrite;
  933.         buffer    = (char *) buffer + towrite;
  934.     }
  935.  
  936.     SPIN(pb->ioResult == inProgress, SP_STREAM_WRITE, 0);
  937.  
  938.     if (!pb->ioResult)
  939.         return(bytes);
  940.     else
  941.         return TCP_error(pb->ioResult);
  942. }
  943.  
  944. int TCPSocket::select(Boolean * canRead, Boolean * canWrite, Boolean *)
  945. {
  946.     int    goodies     =     0;
  947.  
  948.     if (canRead)
  949.         switch (sstate) {
  950.         case SOCK_STATE_LIS_CON:
  951.             *canRead    = true;
  952.             ++goodies;
  953.             break;
  954.         case SOCK_STATE_CONNECTED:
  955.             if (Available()) {
  956.                 *canRead    = true;
  957.                 ++goodies;
  958.             }
  959.             break;
  960.         case SOCK_STATE_UNCONNECTED:
  961.         case SOCK_STATE_CLOSING:
  962.             *canRead    = true;
  963.             ++goodies;
  964.             break;
  965.         }
  966.  
  967.     if (canWrite)
  968.         switch (sstate) {
  969.         case SOCK_STATE_CONNECTING:
  970.             break;
  971.         default:
  972.             *canWrite = true;
  973.             ++goodies;
  974.         }
  975.  
  976.     return goodies;
  977. }
  978.